import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.Component;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPasswordField;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.border.LineBorder;
import javax.swing.JFrame;
import java.awt.Color;



/**
 * Responsible for presenting Jotto visually and reporting user interactions to a 
 * controller.
 * 
 * @author Sam Snyder
 *
 */
public class JottoView extends JFrame implements GameView {
	
	/**
	 * Checkstyle seems to dig UIDs.
	 */
	private static final long serialVersionUID = 8926686399807129527L;
	
	/**
	 * Width of the game window.
	 */
	private static final int FRAMEWIDTH = 600;
	
	/**
	 * Height of the game window.
	 */
	private static final int FRAMEHEIGHT = 800;

	/**
	 * Field that guesses by either player are input to.
	 */
	private JTextField guessBox;

	/**
	 * Field that accompanies the guessBox, indicating which player's turn it is.
	 */
	private JLabel guessLabel;
	
	/**
	 * Displays the history of guesses made against Player2's word by Player1.
	 */
	private JTextArea p1GuessHistory;
	
	/**
	 * Displays the history of guesses made against Player1's word by Player2.
	 */
	private JTextArea p2GuessHistory;
	
	/**
	 * Text area shows how many points correctly guessing the word is worth.
	 */
	private JTextArea scoreTextArea;
	
	
	/**
	 * Displays the number of wins by Player 1.
	 */
	private JLabel p1WinsLabel;
	
	/**
	 * Displays the number of wins by Player 2.
	 */
	private JLabel p2WinsLabel;
	
	/**
	 * Displays Player 1's score.
	 */
	private JLabel p1ScoreLabel;
	
	/**
	 * Displays Player 2's score.
	 */
	private JLabel p2ScoreLabel;
	
	/**
	 * Place where Player 1 enters a secret word.
	 */
	private JPasswordField p1SecretWord;
	
	/**
	 * Place where Player 2 enters a secret word.
	 */
	private JPasswordField p2SecretWord;
	
	/**
	 * Used to report problems to the user.
	 */
	private JLabel userMessageLabel;
	
	/**
	 * Dialog box which contains attribution information about the project programmers.
	 */
	private JDialog aboutDialog;
	
	/**
	 * Dialog box which contains information about the rules of Jotto.
	 */
	private JDialog rulesDialog;

	/**
	 * Initialize a JottoView object. Sets the frame to visible.
	 */
	JottoView() {
		final int lineheight = 30;
		final int titlefontsize = 18; 
		final int smallfontsize = 11;
		final String defaultfont = "Tahoma";

		// They are used to vertically position components without a layout manager
		final int h1 = 30;
		final int h2 = 60;
		final int h3 = 90;
		final int h4 = 120;
		final int h5 = 150;
		//final int h6 = 180;
		final int h7 = 210;
		final int hlast = 690;
		
		// They are used to horizontally position components without a layout manager.
		//final int w1 = 75;
		//final int w2 = 150;
		final int w3 = 225;
		final int w4 = 300;
		//final int w5 = 375;
		final int w6 = 450;
		//final int w7 = 525;
		
		try {
			setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			this.setTitle("Jotto");
			this.setPreferredSize(new java.awt.Dimension(FRAMEWIDTH, FRAMEHEIGHT));
			
			// Title 'JJotto'
			JLabel titleLabel = new JLabel("Jotto");
			titleLabel.setHorizontalAlignment(SwingConstants.CENTER);
			titleLabel.setFont(new java.awt.Font(defaultfont, 0, titlefontsize));
			titleLabel.setBounds(0, 0, h2, lineheight);
			centerHorizontal(titleLabel, w4);
			
			// Vertical Separator
			JSeparator verticalSeparator = new JSeparator();
			final int separatorHeight = 650;
			verticalSeparator.setBounds(FRAMEWIDTH / 2, lineheight, 1, separatorHeight);
			verticalSeparator.setBorder(
					new LineBorder(java.awt.Color.LIGHT_GRAY, 1, false));
			
			// Column labels
			JLabel columnLabels = new JLabel();
			columnLabels.setFont(new java.awt.Font("Monospaced", 0,	smallfontsize));
			columnLabels.setText("   Score       Player2's Guesses    Jots" 
					+ "                  Player1's Guesses    Jots");
			columnLabels.setBounds(0, h4, FRAMEWIDTH, lineheight);

			// Displays the scores associated with getting the answer by a given guess
			final int textareaheight = 525;
			final int gray = 240;
			final int normalfontsize = 14;
			scoreTextArea = new JTextArea("100\n95\n90\n85\n80\n75\n70\n65\n60\n55\n50" 
					+ "\n45\n40\n35\n30\n25\n20\n15\n10\n5");
			scoreTextArea.setBackground(new java.awt.Color(gray, gray, gray));
			scoreTextArea.setFont(new java.awt.Font("Monospaced", 0, normalfontsize));
			scoreTextArea.setBounds(h1, h5, h2, textareaheight);
			scoreTextArea.setEnabled(false);
			scoreTextArea.setDisabledTextColor(Color.BLACK);
			
			// Box for entering guesses
			guessBox = new JTextField();
			guessBox.setBounds(0, hlast, h2, lineheight);
			guessBox.setEnabled(false);
			centerHorizontal(guessBox, w4);
			
			// Label says which player is guessing 
			guessLabel = new JLabel("Player1: ");
			guessLabel.setForeground(Color.BLUE);
			guessLabel.setBounds(0, hlast, h3, lineheight);
			centerHorizontal(guessLabel, w3);
			
			// Label reports problems to the players
			userMessageLabel = new JLabel("Enter secret words to begin");
			userMessageLabel.setBounds(0, hlast, h7, lineheight);
			centerHorizontal(userMessageLabel, w6);
			
			// Add player neutral UI elements
			getContentPane().setLayout(null);
			getContentPane().add(titleLabel);
			getContentPane().add(verticalSeparator);
			getContentPane().add(columnLabels);
			getContentPane().add(scoreTextArea);
			getContentPane().add(guessLabel);
			getContentPane().add(guessBox);
			getContentPane().add(userMessageLabel);
			
			// Add UI Components for Player 1
			p1SecretWord = new JPasswordField();
			p1WinsLabel = new JLabel();
			p1ScoreLabel = new JLabel();
			p2GuessHistory = new JTextArea();			
			createPlayerUI(PlayerSelect.P1, 0,
					p1SecretWord, p1WinsLabel, p1ScoreLabel, p2GuessHistory);

			// Add UI Components for Player 2
			p2SecretWord = new JPasswordField(); 
			p2WinsLabel = new JLabel();
			p2ScoreLabel = new JLabel();
			p1GuessHistory = new JTextArea();
			createPlayerUI(PlayerSelect.P2, w4, 
					p2SecretWord, p2WinsLabel, p2ScoreLabel, p1GuessHistory);

			// Define the MenuBar
			JMenuBar menuBar = new JMenuBar();
			setJMenuBar(menuBar);
			menuBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

			// Add a Menu 
			JMenu gameMenu = new JMenu();
			menuBar.add(gameMenu);
			gameMenu.setText("Game");

			// Add Menu options
			//JMenuItem newgameMenuItem = new JMenuItem("New Game");
			//gameMenu.add(newgameMenuItem);

			JMenuItem rulesMenuItem = new JMenuItem("Rules");
			gameMenu.add(rulesMenuItem);
			rulesMenuItem.setAction(new AbstractAction("Rules", null) {
				private static final long serialVersionUID = 1L;
				public void actionPerformed(ActionEvent evt) {
					getRulesDialog().pack();
					getRulesDialog().setLocationRelativeTo(null);
					getRulesDialog().setVisible(true);
				}
			});

			JMenuItem aboutMenuItem = new JMenuItem("About");
			gameMenu.add(aboutMenuItem);
			aboutMenuItem.setAction(new AbstractAction("About", null) {
				private static final long serialVersionUID = 1L;
				public void actionPerformed(ActionEvent evt) {
					getAboutDialog().pack();
					getAboutDialog().setLocationRelativeTo(null);
					getAboutDialog().setVisible(true);
				}
			});

			// Prepare everything for display
			pack();
			this.setSize(FRAMEWIDTH, FRAMEHEIGHT);
			this.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Allows a guess limit between 2 and 27 to be set. If the guess is outside of those
	 * bounds then the default 20 is used.
	 * 
	 * @param guessLimit number of guesses allowed this game session
	 */
	JottoView(int guessLimit) {
		this();
		
		
		final int maxscore = 100;
		String scoresText = "";
		for (int i = 0; i < guessLimit; i++) {
			scoresText += 
				Math.round(maxscore - i * ((float) maxscore) / guessLimit) + "\n";
		}
		scoreTextArea.setText(scoresText);
	}
	/**
	 * Defines the 'About' Dialog box.
	 * 
	 * @return the 'About' Dialog box.
	 */
	private JDialog getAboutDialog() {
		if (aboutDialog == null) {
			final int dialogWidth = 300;
			final int dialogHeight = 170;
			final String defaultfont = "Tahoma";
			final int largefontsize = 14; 
			
			// Constants used to vertically position elements
			final int h1 = 25;
			final int h2 = 50;
			final int h3 = 75;
			final int h4 = 100;
			
			// Constants used to horizontally position elements
			
			aboutDialog = new JDialog(this);
			aboutDialog.getContentPane().setLayout(null);
			aboutDialog.setTitle("About");
			aboutDialog.setPreferredSize(new java.awt.Dimension(
					dialogWidth, dialogHeight));
			
			JLabel aboutTextLabel = new JLabel();
			aboutTextLabel.setText("    Jotto 1.0 created by the brilliant");
			aboutTextLabel.setBounds(0, 0, dialogWidth, h1);
			
			JLabel attributionLabel1 = new JLabel("Sam Snyder");
			attributionLabel1.setFont(new java.awt.Font(defaultfont, 0, largefontsize));
			attributionLabel1.setHorizontalAlignment(SwingConstants.CENTER);
			attributionLabel1.setBounds(0, h1, dialogWidth, h1);
			
			JLabel attributionLabel2 = new JLabel();
			attributionLabel2.setText("Brian Block");
			attributionLabel2.setFont(new java.awt.Font(defaultfont, 0, largefontsize));
			attributionLabel2.setHorizontalAlignment(SwingConstants.CENTER);
			attributionLabel2.setBounds(0, h2, dialogWidth, h1);
			
			JLabel attributionLabel3 = new JLabel();
			attributionLabel3.setText("Yao Wang");
			attributionLabel3.setFont(new java.awt.Font(defaultfont, 0, largefontsize));
			attributionLabel3.setHorizontalAlignment(SwingConstants.CENTER);
			attributionLabel3.setBounds(0, h3, dialogWidth, h1);
			
			JButton closeAboutButton = new JButton("Awesome");
			final int closebuttonpos = 175;
			closeAboutButton.setBounds(closebuttonpos, h4, h4, h1);
			closeAboutButton.setAction(
				new AbstractAction("Awesome", null) {
				private static final long serialVersionUID = 1L;

				public void actionPerformed(ActionEvent evt) {
					aboutDialog.dispose();
				}
			});
			
			aboutDialog.getContentPane().add(aboutTextLabel);
			aboutDialog.getContentPane().add(attributionLabel1);
			aboutDialog.getContentPane().add(attributionLabel2);
			aboutDialog.getContentPane().add(attributionLabel3);
			aboutDialog.getContentPane().add(closeAboutButton);
			aboutDialog.setSize(dialogWidth, dialogHeight);
		}
		return aboutDialog;
	}
	
	/**
	 * Displays the rules of Jotto to the players.
	 * 
	 * @return Dialog box which contains the rules to Jotto.
	 */
	private JDialog getRulesDialog() {
		if (rulesDialog == null) {
			
			final int dialogWidth = 500;
			final int dialogHeight = 500;
			
			rulesDialog = new JDialog(this);
			rulesDialog.setLayout(null);
			rulesDialog.setSize(dialogWidth, dialogHeight);
			rulesDialog.setPreferredSize(new java.awt.Dimension(
					dialogWidth, dialogHeight));
			rulesDialog.setTitle("Game Rules");
			
			JTextArea rulesText = new JTextArea();
			rulesText.setEditable(false);
			final int bggray = 240;
			rulesText.setBackground(new Color(bggray, bggray, bggray));
			String rules = "Jotto is a word game in which the objective is to guess the\n"
					+ "opposing player's secret word before they can guess yours.\n\n"
					+ "Players begin by each selecting a secret word. This word, \n"
					+ "like all words used in Jotto, must be five letters long \n"
					+ "and contain no duplicate letters. i.e. 'wharf' and 'short'\n"
					+ "are acceptable but 'start' and 'shorter' are not.\n\n"
					+ "Once both"
					+ " players have selected their words one of them\nmakes a guess "
					+ "at the opponents word. The program will tell\nhow many letters"
					+ ", or 'Jots', are shared between the guess\nand the opponent's "
					+ "secret word.\n\nThis continues back and forth until one player "
					+ "succeeds in\nguessing the other's word, at which point that player"
					+ " is\nawarded a win and points corresponding to how many"
					+ "\nguesses were required. The other player continues to guess\nand "
					+ "is awarded points if the word is eventually guessed.\n\n"
					+ "It's possible to have more wins but the lower score.\nWho the real"
					+ "winner is up to the players to work out "
					+ "amongst\nthemselves, possibly via Rock Paper Scissors.\nEnjoy!";
			rulesText.setText(rules);
			final int ruleswidth = 484;
			final int rulesheight = 430;
			rulesText.setBounds(0, 0, ruleswidth, rulesheight);
			
			JButton closeRulesButton = new JButton("Close");
			final int closeXpos = 390;
			final int closeYpos = rulesheight;
			final int closeHeight = 30;
			final int closeWidth = 90;
			closeRulesButton.setBounds(closeXpos, closeYpos, closeWidth, closeHeight);
			closeRulesButton.setAction(
				new AbstractAction("Close", null) {
				private static final long serialVersionUID = 1L;

				public void actionPerformed(ActionEvent evt) {
					rulesDialog.dispose();
				}
			});
			
			rulesDialog.getContentPane().add(rulesText);
			rulesDialog.getContentPane().add(closeRulesButton);
			
		}
		return rulesDialog;
	}
	
	/**
	 * Set's a component's position such that it is horizontally centered around the given
	 * bound.
	 * 
	 * @param c the component to be centered
	 * @param bound the vertical line on which c is centered
	 */
	private void centerHorizontal(Component c, int bound) {
		c.setLocation(bound - c.getWidth() / 2, c.getY());
	}
	
	/**
	 * Sets up all necessary player UI elements for a given player and adds them to the 
	 * Frame. All components are offset from the left edge by the given number of pixels.
	 * The component arguments are used to return references to the caller so that they
	 * can be accessed later. They are all 'produces' mode, to use RESOLVE terminology.
	 * 
	 * @param pl specifies the player in question, used primarily to label things '1' or 
	 * 			 '2'.
	 * @param offset pixels from the left edge to place elements at
	 * @param secretWord the password box that the secret word is entered into
	 * @param winsLabel the JLabel which displays a player's win count.
	 * @param scoreLabel the JLabel which displays a player's score
	 * @param guessHistory the JTextArea
	 *  
	 */
	private void createPlayerUI(PlayerSelect pl, int offset,
			JPasswordField secretWord, JLabel winsLabel, JLabel scoreLabel,
			JTextArea guessHistory) {
		
		
		// These constants correspond to 30px horizontal slices of the window
		// They are used to vertically position components without a layout manager
		// Also used to specify the sizes of elements, when convenient
		final int h1 = 30;
		final int h2 = 60;
		final int h3 = 90;
		final int h4 = 120;
		final int h5 = 150;
		
		// These constants correspond to 75px vertical slices of the window
		// They are used to horizontally position components without a layout manager
		final int s1 = 75;
		final int s2 = 150;
		final int s3 = 225;
		final int w1 = s1 + offset;
		final int w2 = s2 + offset; 
		final int w3 = s3 + offset;
		
		//  Player label
		JLabel playerLabel = new JLabel();
		switch (pl) {
		case P1:
			playerLabel.setText("Player 1");
			playerLabel.setForeground(Color.BLUE);
			break;
		default:
			playerLabel.setText("Player 2");
			playerLabel.setForeground(Color.RED);
		}
		playerLabel.setHorizontalAlignment(SwingConstants.CENTER);
		playerLabel.setBounds(0, h1, h2, h1);
		centerHorizontal(playerLabel, w2);
		
		
		//  Secret word entry box
		final int secretheight = 25;
		secretWord.setBounds(0, h3, h2, secretheight);
		centerHorizontal(secretWord, w2);
		
		//  Secret word label
		JLabel secretLabel = new JLabel();
		secretLabel.setText("Secret Word: ");
		secretLabel.setHorizontalAlignment(SwingConstants.CENTER);
		secretLabel.setBounds(0, h3, h3, h1);
		centerHorizontal(secretLabel, w1);
		
		//  Win counter
		winsLabel.setText("Wins: 0");
		winsLabel.setHorizontalAlignment(SwingConstants.CENTER);
		winsLabel.setBounds(0, h2, h2, h1);
		centerHorizontal(winsLabel, w1);
		
		
		//  Score counter
		scoreLabel.setText("Score: 0");
		scoreLabel.setHorizontalAlignment(SwingConstants.CENTER);
		scoreLabel.setBounds(0, h2, h3, h1);
		centerHorizontal(scoreLabel, w3);
		
		
		//  Horizontal separator
		JSeparator horizSep = new JSeparator();
		final int separatorWidth = 260;
		horizSep.setBounds(0, h4, separatorWidth, 1);
		horizSep.setBorder(
				new LineBorder(Color.LIGHT_GRAY, 1, false));
		centerHorizontal(horizSep, w2);

		final int textareawidth = 140;
		final int textareaheight = 525;
		
		// Displays guesses made against Player 
		final int guessHistoryOffset = 200;
		final int guessHistoryPosition = guessHistoryOffset + offset;
		final int bggray = 240;
		final int normalfontsize = 14;
		guessHistory.setFont(new java.awt.Font("Monospaced", 0,	normalfontsize));
		guessHistory.setBackground(new Color(bggray, bggray, bggray));
		guessHistory.setBounds(0, h5, textareawidth, textareaheight);
		guessHistory.setEnabled(false);
		guessHistory.setDisabledTextColor(Color.BLACK);
		centerHorizontal(guessHistory, guessHistoryPosition);
		
		getContentPane().add(playerLabel);
		getContentPane().add(secretWord);
		getContentPane().add(secretLabel);
		getContentPane().add(winsLabel);
		getContentPane().add(scoreLabel);
		getContentPane().add(horizSep);
		getContentPane().add(winsLabel);
		getContentPane().add(guessHistory);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addGuessListener(KeyListener kl) {
		guessBox.addKeyListener(kl);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addSecretListener(PlayerSelect p, KeyListener kl) {
		switch (p) {
		case P1:
			p1SecretWord.addKeyListener(kl);
			break;
		default:
			p2SecretWord.addKeyListener(kl);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void reset() {
		p1ScoreLabel.setText("Score: 0");
		p2ScoreLabel.setText("Score: 0");
		p1WinsLabel.setText("Wins: 0");
		p2WinsLabel.setText("Wins: 0");
		p1SecretWord.setEditable(true);
		p2SecretWord.setEditable(true);
		guessBox.setEditable(false);
		p1GuessHistory.setText("");
		p2GuessHistory.setText("");
		guessLabel.setText("Player 1 Guess: ");
		guessLabel.setForeground(Color.BLUE);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setScore(PlayerSelect p, int points) {
		JLabel scoreLabel;
		switch (p) {
		case P1:
			scoreLabel = p1ScoreLabel;
			break;
		default:
			scoreLabel = p2ScoreLabel;
			break;	
		}
		scoreLabel.setText("Score: " + points);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setWins(PlayerSelect p, int wins) {
		JLabel winsLabel;
		switch (p) {
		case P1:
			winsLabel = p1WinsLabel;
			break;
		default:
			winsLabel = p2WinsLabel;
			break;	
		}
		winsLabel.setText("Wins: " + wins);
	}
	
	/**
	 * Retrieves the text currently entered by the user into the guessBox. 
	 * 
	 * @return the text of the guessBox
	 */
	@Override
	public String getGuessText() {
		return guessBox.getText();
	}
	
	/**
	 * Sets the text inside the guessBox. Allows the GameController to enforce input 
	 * rules.
	 * 
	 * @param text the text being set to the guessBox.
	 */
	@Override
	public void setGuessText(String text) {
		guessBox.setText(text);
	}

	/**
	 * Retrieves a player's secret text from their JPasswordField.
	 * 
	 * @param p the player having their secret text retrieved
	 * @return the contents of the JPasswordField as a String
	 */
	@Override
	public String getSecretText(PlayerSelect p) {
		switch (p) {
		case P1:
			return new String(p1SecretWord.getPassword());
		default:
			return new String(p2SecretWord.getPassword());
		}
	}

	/**
	 * Sets the text and color of the JLabel associated with the guessBox.
	 * This is used to indicate which player's turn it is to the users.
	 * 
	 * @param text the String to be set as the guessBox's contents
	 * @param c the color that
	 */
	@Override
	public void setGuessLabel(String text, Color c) {
		guessLabel.setText(text);
		guessLabel.setForeground(c);
	}

	/**
	 * Sets a player's secret word in their JPasswordField.
	 * 
	 * @param p the player having their secret text altered
	 * @param text the String replacing the old text
	 */
	@Override
	public void setSecretText(PlayerSelect p, String text) {
		switch(p) {
		case P1:
			p1SecretWord.setText(text);
			break;
		default:
			p2SecretWord.setText(text);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setGuessEditable(boolean editable) {
		guessBox.setEnabled(editable);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setSecretEditable(PlayerSelect p, boolean editable) {
		switch (p) {
		case P1:
			p1SecretWord.setEnabled(editable);
			break;
		default:
			p2SecretWord.setEnabled(editable);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setUserMessage(String text) {
		userMessageLabel.setText(text);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addGuessHistory(PlayerSelect p, String word, int matched) {
		String currenttext = "";
		switch (p) {
		case P1:
			currenttext = p1GuessHistory.getText();
			p1GuessHistory.setText(currenttext 
					+ word + "           " + matched + "\n");
			break;
		default:
			currenttext = p2GuessHistory.getText();
			p2GuessHistory.setText(currenttext 
					+ word + "           " + matched + "\n");
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void clearGuessHistories() {
		p1GuessHistory.setText("");
		p2GuessHistory.setText("");
		
	}
}
